const api = require('../../utils/api.js').default

Page({
  data: {
    ipBgUrl: '',
    imgBaseUrl: '',
    robotAvatar: '',
    avatarUrl: '',
    sessionList: [],
    isHistoryShow: false,
    isLoadingHistory: false,
    videoContext: null,
    messages: [{
      role: 'robot',
      content: '同学你好！有什么问题尽管问我！'
    }],
    inputValue: '',
    isSending: false,
    isTyping: false,
    showTimeDivider: true,
    currentTime: '',
    isLoading: false,
    scrollTop: 0,
    websocket: null,
    isConnected: false,
    userId: '',
    userToken: '',
    currentReply: '',
    autoFocus: false,
    sessionId: '',
    // 语音播放相关数据
    audioContext: null,
    isPlaying: false,
    // 语音识别相关数据
    isVoiceMode: false, // 是否为语音模式
    recording: false,   // 是否正在录音
    recordStartTime: 0, // 录音开始时间
    recordTimer: null,  // 录音定时器
    recordManager: null, // 录音管理器
    // 性别角色相关数据
    gender: 'male', // 默认为男性
    voiceType: 5003 // 男性声音类型
  },

  // 页面渲染完成后初始化视频上下文
  onReady() {
    this.setData({
      videoContext: wx.createVideoContext('chatBgvideo', this),
      audioContext: wx.createInnerAudioContext(),
      recordManager: wx.getRecorderManager()
    });
    
    // 音频事件监听
    this.data.audioContext.onPlay(() => {
      this.setData({ isPlaying: true });
      // 音频开始播放时启动人物动画
      this.startCharacterAnimation();
    });
    
    this.data.audioContext.onStop(() => {
      this.setData({ isPlaying: false });
    });
    
    this.data.audioContext.onPause(() => {
      this.setData({ isPlaying: false });
    });
    
    this.data.audioContext.onEnded(() => {
      this.setData({ isPlaying: false });
      // 检查是否还有待播放的音频片段
      if (!this.hasPendingAudioSegments()) {
        // 如果没有待播放的音频，则停止视频
        this.stopCharacterAnimation();
      }
    });
    
    this.data.audioContext.onError((res) => {
      console.error('音频播放错误', res);
      this.setData({ isPlaying: false });
      wx.showToast({
        title: '语音播放失败',
        icon: 'none'
      });
    });
    
    // 录音事件监听
    this.data.recordManager.onStop((res) => {
      console.log('录音结束', res);
      this.setData({ recording: false });
      
      if (res.duration < 1000) {
        wx.showToast({
          title: '录音时间太短',
          icon: 'none'
        });
        return;
      }
      
      // 将录音文件转为base64并发送
      this.convertAudioToBase64(res.tempFilePath, res.duration);
    });
    
    this.data.recordManager.onStart(() => {
      console.log('录音开始');
      this.setData({ 
        recording: true,
        recordStartTime: Date.now()
      });
    });
    
    this.data.recordManager.onError((res) => {
      console.error('录音错误', res);
      this.setData({ recording: false });
      wx.showToast({
        title: '录音失败',
        icon: 'none'
      });
    });
    
    // 页面加载完成后播放视频
    this.playVideo();
  },

  onLoad() {
    // 从全局变量获取性别设置
    const app = getApp();
    const gender = app.globalData.gender || "male";
    const voiceType = gender === 'male' ? 5003 : 0;
    
    const imgBaseUrl = api.ENV.imgBaseUrl;
    const videoBaseUrl = api.ENV.videoBaseUrl;
    // 默认使用挥手背景视频
    const ipBgUrl = videoBaseUrl + (gender === 'male' ? 'male-waving1.mp4' : 'female-waving.mp4');
    
    this.setData({
      imgBaseUrl,
      videoBaseUrl: videoBaseUrl,
      // 根据性别设置不同的头像
      robotAvatar: imgBaseUrl + (gender === 'male' ? 'avatar_male.png' : 'avatar_female.png'),
      ipBgUrl: ipBgUrl,
      gender: gender,
      voiceType: voiceType
    });

    this.updateTimeDivider();
    const userinfo = wx.getStorageSync('userInfo');
    if (userinfo === null) {
      wx.navigateTo({ url: '/pages/welcome/welcome' });
    }
    this.setData({
      autoFocus: true,
      avatarUrl: userinfo.avatarUrl || ''
    });

    const sessionId = this.generateUUID();
    this.setData({ sessionId });
    console.log("当前会话ID：", sessionId);

    // 初始化片段计数器
    this.segmentCounter = 0;

    if (app && app.globalData) app.globalData.isUnloaded = false;

    this.loadLoginInfo();
    this.loadMoreHistory();
    
    // 监听用户切换事件
    this.handleUserSwitch();
  },

  generateUUID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
      const r = Math.random() * 16 | 0;
      const v = c === 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
    });
  },

  // 切换性别角色
  switchGender() {
    const newGender = this.data.gender === 'male' ? 'female' : 'male';
    const newVoiceType = newGender === 'male' ? 5003 : 0;
    // 切换时也使用挥手背景视频
    const newBgUrl = this.data.videoBaseUrl + (newGender === 'male' ? 'male-waving1.mp4' : 'female-waving.mp4');
    
    this.setData({
      gender: newGender,
      voiceType: newVoiceType,
      ipBgUrl: newBgUrl,
      // 同时更新头像
      robotAvatar: this.data.imgBaseUrl + (newGender === 'male' ? 'avatar_male.png' : 'avatar_female.png')
    });
    
    wx.showToast({
      title: newGender === 'male' ? '已切换为男性角色' : '已切换为女性角色',
      icon: 'none'
    });
  },

  // 加载登录信息
  loadLoginInfo() {
    try {
      const loginResult = wx.getStorageSync('loginResult') || '';
      console.log("Retrieved loginResult:", loginResult); // 添加调试日志
      if (loginResult) {
        console.log("Setting userToken and userId from loginResult"); // 添加调试日志
        this.setData({
          userToken: loginResult.userToken,
          userId: loginResult.userId
        });
        this.connectWebSocket();
      } else {
        console.error("未找到 loginResult");
        // 尝试从其他位置获取用户信息
        const userinfo = wx.getStorageSync('userInfo');
        console.log("Retrieved userinfo:", userinfo); // 添加调试日志
        if (userinfo) {
          const userId = userinfo.userId || userinfo.openId;
          console.log("Setting userId from userinfo:", userId); // 添加调试日志
          this.setData({
            userId: userId,
          });
        }
        this.connectWebSocket();
      }
    } catch (e) {
      console.error("加载登录信息失败:", e);
    }
  },

  // 建立WebSocket连接
  connectWebSocket() {
    const { userId } = this.data;
    const wsUrl = api.WS_URL;

    // 如果userId为空，则不尝试连接WebSocket
    if (!userId) {
      console.warn("userId为空，无法建立WebSocket连接");
      return;
    }

    if (this.data.websocket) {
      this.data.websocket.close();
      this.setData({ websocket: null, isConnected: false });
    }

    const websocket = wx.connectSocket({
      url: wsUrl,
      header: api.mapiHeaders,
      method: "GET"
    });

    this.setData({ websocket });

    websocket.onOpen(() => {
      console.log("WebSocket连接已建立");
      this.setData({ isConnected: true });

      const signInCmd = `SignIn ${userId}`;
      websocket.send({
        data: signInCmd,
        success: () => console.log(`已发送SignIn命令：${signInCmd}`)
      });
    });

    websocket.onMessage((res) => {
      const message = res.data;
      console.log(`收到WebSocket消息：${message}`);
      this.handleWebSocketMessage(message);
    });

    websocket.onClose((res) => {
      console.log("WebSocket连接已关闭", res);
      this.setData({
        isConnected: false,
        websocket: null
      });
      const app = getApp();
      if (!app.globalData.isUnloaded) {
        setTimeout(() => this.connectWebSocket(), 3000);
      }
    });

    websocket.onError((err) => {
      console.error("WebSocket错误:", err);
      this.setData({ isConnected: false });
    });
  },

  // 处理WebSocket消息
  handleWebSocketMessage(message) {
    const { websocket, userId } = this.data;

    if (message.startsWith("SignIn ")) {
      if (message.includes("SignIn OK")) {
        console.log("SignIn验证成功，发送AttachChat命令");
        const attachChatCmd = `AttachChat ${userId}`;
        websocket.send({
          data: attachChatCmd,
          success: () => console.log(`已发送AttachChat命令：${attachChatCmd}`)
        });
      } else {
        console.error("SignIn验证失败：", message);
      }
      return;
    }

    if (message.startsWith("AttachChat ")) {
      if (message.includes("AttachChat OK")) {
        console.log("AttachChat订阅成功");
      } else {
        console.error("AttachChat订阅失败：", message);
      }
      return;
    }

    // ✅ 核心：流式接收 AI 输出
    if (message.startsWith("ChatResult")) {
      try {
        const commandPrefix = "ChatResult ";
        const afterPrefix = message.slice(commandPrefix.length);
        const spaceIndex = afterPrefix.indexOf(" ");
        if (spaceIndex === -1) throw new Error(`消息格式错误：${afterPrefix}`);

        const status = afterPrefix.slice(0, spaceIndex);
        const jsonStr = afterPrefix.slice(spaceIndex + 1);
        if (status !== "OK") return console.warn(`AI返回状态异常：${status}`);

        const chatData = JSON.parse(jsonStr);
        const content = (chatData.content || "").toString();
        const isEnd = chatData.isEnd === true;
        const order = chatData.order || 0;

        // === 开始回复 ===
        if (order === 0) {
          this.currentReply = "";
          const updatedMessages = [...this.data.messages, {
            role: "robot",
            content: "",
            time: new Date().toLocaleTimeString()
          }];
          this.setData({ messages: updatedMessages, isTyping: true });

          // 🎬 启动人物动画
          this.startCharacterAnimation();
          
          // 初始化语音播放相关变量
          this.clearAudioQueue(); // 清空之前的音频队列，确保新对话的独立性
          
          // 重置片段计数器
          this.segmentCounter = 0;
        }

        // === 累积内容 ===
        if (content) this.currentReply += content;

        const messages = this.data.messages;
        const lastIndex = messages.length - 1;
        if (lastIndex >= 0 && messages[lastIndex].role === "robot") {
          messages[lastIndex].content = this.currentReply;
          this.setData({ messages });
        }

        this.scrollToBottom();

        // === 在回复过程中就开始语音播放 ===
        this.processReplyForTTS(content);
        
        // === 回复结束 ===
        if (isEnd) {
          this.setData({ isTyping: false });
          const replyLen = this.currentReply.length;
          this.currentReply = "";

          // 处理剩余的缓冲区内容
          this.processRemainingTTSBuffer();
          
          if (replyLen > 50) {
            this.loopCharacterAnimationOnce();
          } else {
            // 文本输出完成后继续播放视频直到所有音频播放完成
            // 不再立即停止视频，而是等待所有音频播放完成
          }
        }

      } catch (error) {
        console.error("解析ChatResult失败：", error);
      }
      return;
    }

    console.warn("收到未知类型的WebSocket消息：", message);
  },
  
  // 处理回复内容用于语音播放
  processReplyForTTS(newContent) {
    if (!newContent || newContent.length === 0) return;
    
    // 将新内容添加到缓冲区
    if (!this.ttsBuffer) {
      this.ttsBuffer = "";
    }
    this.ttsBuffer += newContent;
    
    // 定期清理临时文件，避免存储空间耗尽
    this.checkAndCleanStorage();
    
    // 定义句子结束标点符号
    const sentenceEndings = ['。', '！', '？', '\n','，'];
    
    // 检查缓冲区中是否有完整的句子
    while (true) {
      let sentenceEndIndex = -1;
      
      // 查找第一个句子结束符的位置
      for (let i = 0; i < this.ttsBuffer.length; i++) {
        if (sentenceEndings.includes(this.ttsBuffer[i]) && i>15) {
          sentenceEndIndex = i;
          break;
        }
      }
      
      // 如果找到了句子结束符
      if (sentenceEndIndex !== -1) {
        // 提取完整的句子（包括结束符）
        const sentence = this.ttsBuffer.substring(0, sentenceEndIndex + 1);
        this.ttsBuffer = this.ttsBuffer.substring(sentenceEndIndex + 1);
        
        // 处理这个句子
        this.processTTSSegment(sentence);
      } else {
        // 没有找到句子结束符，检查缓冲区是否太长
        if (this.ttsBuffer.length > 100) {
          // 如果缓冲区太长（超过100字符），强制分割
          const segment = this.ttsBuffer.substring(0, 100);
          this.ttsBuffer = this.ttsBuffer.substring(100);
          this.processTTSSegment(segment);
        } else {
          // 缓冲区不够长，等待更多内容
          break;
        }
      }
    }
  },
  
  // 处理剩余的缓冲区内容（在回复结束时调用）
  processRemainingTTSBuffer() {
    if (this.ttsBuffer && this.ttsBuffer.length > 0) {
      // 处理剩余的所有文本
      this.processTTSSegment(this.ttsBuffer);
      this.ttsBuffer = ""; // 清空缓冲区
    }
  },
  
  // 检查是否还有待播放的音频片段
  hasPendingAudioSegments() {
    if (!this.pendingSegments || this.pendingSegments.length === 0) {
      return false;
    }
    
    // 检查是否有未播放完的片段
    return this.pendingSegments.some(segment => !segment.isPlayed);
  },
  
  // 处理单段文本的语音合成
  processTTSSegment(segment) {
    // 文本预处理：将652替换为六五二
    const processedSegment = segment.replace(/652/g, '六五二');
    
    const { userToken, voiceType } = this.data;
    
    // 为每个片段分配唯一ID，确保顺序播放
    // 使用递增的计数器确保严格顺序
    if (typeof this.segmentCounter === 'undefined') {
      this.segmentCounter = 0;
    }
    this.segmentCounter++;
    const segmentId = this.segmentCounter; // 确保时间戳部分不会影响顺序
    
    // 创建一个待处理的片段对象（只在需要时创建）
    if (!this.pendingSegments) {
      this.pendingSegments = [];
    }
    
    // 检查是否已存在相同的片段（避免重复处理）
    const existingSegment = this.pendingSegments.find(s => s.segment === processedSegment && !s.base64);
    if (existingSegment) {
      return; // 避免重复处理相同的片段
    }
    
    const pendingSegment = {
      id: segmentId,
      segment: processedSegment,
      base64: null,
      isPlaying: false,
      isPlayed: false
    };
    
    this.pendingSegments.push(pendingSegment);
    
    // 调用短文本语音合成API
    this.callTTSAPI(processedSegment, userToken, 0, segmentId, voiceType);
  },
  
  // 调用TTS API，支持重试机制
  callTTSAPI(segment, userToken, retryCount, segmentId, voiceType = 0) {
    const maxRetries = 2; // 减少重试次数以降低资源消耗
    
    wx.request({
      url: 'https://test.uu163yun.com/mapi/Helper/Aip/CreateShortTts',
      method: 'POST',
      timeout: 10000, // 恢复到10秒超时
      header: {
        ...api.mapiHeaders(userToken, { ApiVersion: '5.0' })
      },
      data: {
        text: segment,
        voice: voiceType, // 使用当前角色的声音类型
        type: 'Baidu',
        format: 'mp3'
      },
      success: (res) => {
        // 只要返回了base64数据就进行播放，不检查format字段
        if (res.data && res.data.base64) {
          // 语音合成成功，将base64数据添加到播放队列
          
          // 找到对应的片段并更新其base64数据
          if (this.pendingSegments) {
            console.log(this.pendingSegments)
            const targetSegment = this.pendingSegments.find(s => s.id === segmentId);
            if (targetSegment) {
              targetSegment.base64 = res.data.base64;
              
              // 尝试播放队列中的下一个音频
              this.tryPlayNextSegment();
            }
          }
        } else if (res.data && res.data.result && res.data.reason) {
          // 处理API错误响应
          console.error('语音合成失败：', res.data.reason);
          // 如果是超时并且还有重试次数，则进行重试
          if ((res.data.reason.includes('超时') || res.data.reason.includes('timeout')) && retryCount < maxRetries) {
            setTimeout(() => {
              this.callTTSAPI(segment, userToken, retryCount + 1, segmentId, voiceType);
            }, 500 * (retryCount + 1)); // 减少延迟重试时间
          }
        } else if (res.data && res.data.result === "exception") {
          // 特别处理操作超时等异常
          console.error('语音合成异常：', res.data.reason || '操作超时');
          // 如果是超时并且还有重试次数，则进行重试
          if (((res.data.reason && res.data.reason.includes('超时')) || 
               (res.data.reason && res.data.reason.includes('timeout'))) && 
              retryCount < maxRetries) {
            setTimeout(() => {
              this.callTTSAPI(segment, userToken, retryCount + 1, segmentId, voiceType);
            }, 500 * (retryCount + 1)); // 减少延迟重试时间
          }
        }
      },
      fail: (err) => {
        console.error('语音合成请求失败：', err);
        // 如果还有重试次数，则进行重试
        if (retryCount < maxRetries) {
          setTimeout(() => {
            this.callTTSAPI(segment, userToken, retryCount + 1, segmentId, voiceType);
          }, 500 * (retryCount + 1)); // 减少延迟重试时间
        }
      }
    });
  },
  
  // 尝试播放队列中的下一个音频片段
  tryPlayNextSegment() {
    if (!this.pendingSegments || this.pendingSegments.length === 0) {
      // 检查是否所有音频都播放完了，如果是则停止视频
      if (!this.hasPendingAudioSegments()) {
        this.stopCharacterAnimation();
      }
      return;
    }
    
    // 按照添加到队列的顺序查找第一个未播放且已有base64数据的片段
    // 确保严格按照ID顺序播放
    const sortedSegments = this.pendingSegments
      .filter(s => s.base64 && !s.isPlaying && !s.isPlayed)
      .sort((a, b) => a.id - b.id);
    
    // 获取ID最小的片段
    const nextSegment = sortedSegments.length > 0 ? sortedSegments[0] : null;
    
    if (nextSegment && !this.isAudioPlaying) {
      this.isAudioPlaying = true;
      nextSegment.isPlaying = true;
      console.log(nextSegment);
      // 播放音频
      this.playAudioFromBase64(nextSegment.base64, 'mp3', () => {
        // 标记为已播放
        nextSegment.isPlayed = true;
        nextSegment.isPlaying = false;
        this.isAudioPlaying = false;
        
        // 检查是否还有待播放的音频
        if (this.hasPendingAudioSegments()) {
          // 继续尝试播放下一个片段
          this.tryPlayNextSegment();
        } else {
          // 所有音频播放完毕，停止视频
          this.stopCharacterAnimation();
        }
      });
    }
  },
  
  // 从base64数据播放音频
  playAudioFromBase64(base64Data, format, onComplete) {
    if (!base64Data) {
      console.warn('Base64音频数据为空');
      if (onComplete) onComplete();
      return;
    }
    
    // 检查base64数据是否有效
    if (base64Data.length < 100) {
      console.warn('Base64音频数据可能无效');
      wx.showToast({
        title: '音频数据无效',
        icon: 'none'
      });
      if (onComplete) onComplete();
      return;
    }
    
    // 检查存储空间并在必要时清理旧文件
    this.checkAndCleanStorage();
    
    try {
      // 将base64转换为临时文件
      const fs = wx.getFileSystemManager();
      const fileExtension = format && format.toLowerCase() === 'wav' ? 'wav' : 'mp3';
      const filePath = `${wx.env.USER_DATA_PATH}/temp_audio_${Date.now()}_${Math.floor(Math.random() * 1000)}.${fileExtension}`;
      
      fs.writeFile({
        filePath: filePath,
        data: base64Data,
        encoding: 'base64',
        success: () => {
          console.log('临时音频文件写入成功:', filePath);
          // 监听音频播放完成事件
          const onEnded = () => {
            this.data.audioContext.offEnded(onEnded);
            console.log('音频播放完成');
            // 播放完成后删除临时文件
            try {
              fs.unlinkSync(filePath);
              console.log("已删除")
            } catch (e) {
              console.warn('删除临时音频文件失败:', e);
            }
            if (onComplete) onComplete();
          };
          
          this.data.audioContext.onEnded(onEnded);
          
          // 播放音频
          this.data.audioContext.src = filePath;
          this.data.audioContext.play();
        },
        fail: (err) => {
          console.error('写入临时音频文件失败：', err);
          // 如果是因为存储空间不足，尝试清理后再重试一次
          if (err.errMsg && err.errMsg.includes('storage limit')) {
            this.cleanTemporaryFiles();
            // 重试一次
            setTimeout(() => {
              fs.writeFile({
                filePath: filePath,
                data: base64Data,
                encoding: 'base64',
                success: () => {
                  console.log('临时音频文件写入成功:', filePath);
                  // 监听音频播放完成事件
                  const onEnded = () => {
                    this.data.audioContext.offEnded(onEnded);
                    console.log('音频播放完成');
                    // 播放完成后删除临时文件
                    try {
                      fs.unlinkSync(filePath);
                    } catch (e) {
                      console.warn('删除临时音频文件失败:', e);
                    }
                    if (onComplete) onComplete();
                  };
                  
                  this.data.audioContext.onEnded(onEnded);
                  
                  // 播放音频
                  this.data.audioContext.src = filePath;
                  this.data.audioContext.play();
                },
                fail: (err2) => {
                  console.error('第二次尝试写入临时音频文件也失败了：', err2);
                  this.forceCleanAllTemporaryFiles();
                  if (onComplete) onComplete();
                }
              });
            }, 100);
          } else {
            if (onComplete) onComplete();
          }
        }
      });
    } catch (e) {
      console.error('处理音频数据时发生异常：', e);
      wx.showToast({
        title: '音频处理失败',
        icon: 'none'
      });
      if (onComplete) onComplete();
    }
  },
  
  // 检查并清理存储空间
  checkAndCleanStorage() {
    if (!this.lastCleanTime || Date.now() - this.lastCleanTime > 15000) { 
      this.cleanTemporaryFiles();
      this.lastCleanTime = Date.now();
    }
  },
  
  // 清理临时音频文件
  cleanTemporaryFiles() {
    try {
      const fs = wx.getFileSystemManager();
      const fileList = fs.readdirSync(wx.env.USER_DATA_PATH);
      
      // 删除旧的临时音频文件
      fileList.forEach(file => {
        if (file.startsWith('temp_audio_')) {
          const filePath = `${wx.env.USER_DATA_PATH}/${file}`;
          try {
            const stat = fs.statSync(filePath);
            // 删除超过30秒的临时文件（原来是1分钟，现在改为30秒以更积极地清理）
            if (stat.isFile() && Date.now() - stat.lastAccessedTime > 30000) {
              fs.unlinkSync(filePath);
              console.log('已删除旧的临时音频文件:', filePath);
            }
          } catch (e) {
            console.warn('检查或删除临时文件失败:', filePath, e);
          }
        }
      });
    } catch (e) {
      console.warn('清理临时文件时出错:', e);
    }
  },
  
  // 强制清理所有临时音频文件（用于存储空间严重不足的情况）
  forceCleanAllTemporaryFiles() {
    try {
      const fs = wx.getFileSystemManager();
      const fileList = fs.readdirSync(wx.env.USER_DATA_PATH);
      
      // 删除所有临时音频文件
      fileList.forEach(file => {
        if (file.startsWith('temp_audio_')) {
          const filePath = `${wx.env.USER_DATA_PATH}/${file}`;
          try {
            fs.unlinkSync(filePath);
            console.log('强制删除临时音频文件:', filePath);
          } catch (e) {
            console.warn('强制删除临时文件失败:', filePath, e);
          }
        }
      });
    } catch (e) {
      console.warn('强制清理临时文件时出错:', e);
    }
  },
  
  // 清空音频队列，实现伪打断功能
  clearAudioQueue() {
    
    // 清空待处理的文本片段
    if (this.pendingSegments) {
      this.pendingSegments = [];
    }
    
    // 重置缓冲区
    if (this.ttsBuffer) {
      this.ttsBuffer = "";
    }
    
    // 停止所有活跃的音频上下文
    if (this.activeAudioContexts) {
      this.activeAudioContexts.forEach(context => {
        try {
          context.stop();
          context.destroy();
        } catch (e) {
          console.warn('停止音频上下文时出错:', e);
        }
      });
      this.activeAudioContexts = [];
    }
    
    // 重置播放状态
    this.isAudioPlaying = false;
    
    // 重置片段计数器
    this.segmentCounter = 0;
    
    // 清理临时文件
    this.cleanTemporaryFiles();
    
    // 停止视频播放
    this.stopCharacterAnimation();
  },
  
  // ==== 🎞️ 人物动画控制 ====
  startCharacterAnimation() {
    const video = this.data.videoContext;
    if (!video) return;
    try {
      // 根据当前性别选择对应的说话背景视频
      const speakingVideo = this.data.gender === 'male' 
        ? this.data.videoBaseUrl + 'male_speaking.mp4' 
        : this.data.videoBaseUrl + 'female_speaking1.mp4';
      
      // 更新视频源
      this.setData({
        ipBgUrl: speakingVideo
      });
      
      // 等待视频源更新后再播放
      setTimeout(() => {
        video.seek(0);
        video.play();
        console.log("人物动画开始播放");
      }, 100);
    } catch (err) {
      console.warn("启动人物动画失败:", err);
    }
  },

  loopCharacterAnimationOnce() {
    const video = this.data.videoContext;
    if (!video) return;
    try {
      // 根据当前性别选择对应的说话背景视频
      const speakingVideo = this.data.gender === 'male' 
        ? this.data.videoBaseUrl + 'male_speaking.mp4' 
        : this.data.videoBaseUrl + 'female_speaking1.mp4';
      
      // 更新视频源
      this.setData({
        ipBgUrl: speakingVideo
      });
      
      // 等待视频源更新后再播放
      setTimeout(() => {
        video.seek(0);
        video.play();
        clearTimeout(this._stopTimer);
        this._stopTimer = setTimeout(() => {
          try {
            // 播放完成后切换回挥手背景
            this.setIdleCharacterAnimation();
          } catch (e) { }
        }, 3000); // 3秒后暂停，可按视频长度调整
      }, 100);
    } catch (err) {
      console.warn("播放动画失败:", err);
    }
  },

  // 当文本输出完成后继续播放视频直到自然结束
  continueCharacterAnimation() {
    const video = this.data.videoContext;
    if (!video) return;
    try {
      // 根据当前性别选择对应的说话背景视频
      const speakingVideo = this.data.gender === 'male' 
        ? this.data.videoBaseUrl + 'male_speaking.mp4' 
        : this.data.videoBaseUrl + 'female_speaking1.mp4';
      
      // 更新视频源
      this.setData({
        ipBgUrl: speakingVideo
      });
      
      // 等待视频源更新后再播放
      setTimeout(() => {
        video.play();
        console.log("人物动画继续播放直到自然结束");
      }, 100);
    } catch (err) {
      console.warn("继续播放动画失败:", err);
    }
  },

  stopCharacterAnimation() {
    const video = this.data.videoContext;
    if (!video) return;
    try {
      video.pause();
      // 播放完成后切换回挥手背景
      this.setIdleCharacterAnimation();
      console.log("人物动画停止");
    } catch (err) {
      console.warn("暂停动画失败:", err);
    }
  },
  
  // 设置空闲状态下的挥手背景动画
  setIdleCharacterAnimation() {
    const wavingVideo = this.data.gender === 'male' 
      ? this.data.videoBaseUrl + 'male-waving1.mp4' 
      : this.data.videoBaseUrl + 'female-waving.mp4';
    
    this.setData({
      ipBgUrl: wavingVideo
    });
    
    const video = this.data.videoContext;
    if (video) {
      setTimeout(() => {
        video.seek(0);
        video.play();
      }, 100);
    }
  },

  // 视频播放结束事件处理
  onVideoEnded() {
    console.log("背景视频播放结束");
    // 视频播放结束后切换回挥手背景
    this.setIdleCharacterAnimation();
  },

  // ==== 🔊 语音播放功能 ====
  speakText(text) {
    // 调用后端API创建短文本语音合成
    const { userToken, voiceType } = this.data;
    
    // 文本预处理：将652替换为六五二
    const processedText = this.preprocessText(text);
    
    // 限制文本长度以符合短文本接口要求
    const maxLength = 50; // 每段最多25个字符，更适合短文本API
    
    // 将长文本分段处理
    const segments = this.splitTextIntoSegments(processedText, maxLength);
    
    if (segments.length > 0) {
      console.log(`文本将分${segments.length}段进行语音合成`);
      // 创建一个队列来存储待播放的音频
      this.audioQueue = [];
      this.isAudioPlaying = false;
      // 开始处理第一段文本
      this.processFirstSegment(segments, 0, userToken, voiceType);
    } else {
      wx.showToast({
        title: '无可播放文本',
        icon: 'none'
      });
    }
  },
  
  // 处理第一段文本并开始播放
  processFirstSegment(segments, index, userToken, voiceType) {
    if (index >= segments.length) {
      console.log('没有文本段落需要处理');
      return;
    }
    
    const currentSegment = segments[index];
    console.log(`正在处理第${index + 1}/${segments.length}段文本:`, currentSegment);
    
    // 调用短文本语音合成API
    wx.request({
      url: 'https://test.uu163yun.com/mapi/Helper/Aip/CreateShortTts',
      method: 'POST',
      timeout: 10000,
      header: {
        ...api.mapiHeaders(userToken, { ApiVersion: '5.0' }),
        'content-type': 'application/x-www-form-urlencoded'
      },
      data: {
        text: currentSegment,
        voice: voiceType, // 使用当前角色的声音类型
        type: 'Baidu',
        format: 'mp3'
      },
      success: (res) => {
        console.log(`第${index + 1}段语音合成响应:`, res);
        // 只要返回了base64数据就进行播放，不检查format字段
        if (res.data && res.data.base64) {
          // 语音合成成功，base64转为临时文件播放
          console.log(`第${index + 1}段语音合成完成`);
          
          // 立即播放第一段音频
          this.playAudioFromBase64(res.data.base64, 'mp3', () => {
            // 第一段播放完成后，继续处理并播放下一段
            this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
          });
        } else if (res.data && res.data.result && res.data.reason) {
          // 处理API错误响应
          console.error(`第${index + 1}段语音合成失败：`, res.data.reason);
          wx.showToast({
            title: res.data.reason || '语音合成失败',
            icon: 'none'
          });
          // 继续处理下一段
          this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
        } else if (res.data && res.data.result === "exception") {
          // 特别处理操作超时等异常
          const errorMsg = res.data.reason || '操作超时，请稍后重试';
          console.error(`第${index + 1}段语音合成异常：`, errorMsg);
          wx.showToast({
            title: errorMsg,
            icon: 'none'
          });
          // 继续处理下一段
          this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
        } else {
          console.error(`第${index + 1}段语音合成失败：`, res);
          wx.showToast({
            title: '语音合成失败',
            icon: 'none'
          });
          // 继续处理下一段
          this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
        }
      },
      fail: (err) => {
        console.error(`第${index + 1}段语音合成请求失败：`, err);
        wx.showToast({
          title: '网络请求失败',
          icon: 'none'
        });
        // 继续处理下一段
        this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
      }
    });
  },
  
  // 处理并播放下一段音频
  processAndPlayNextSegment(segments, index, userToken, voiceType) {
    // 检查是否还有段落需要处理
    if (index >= segments.length) {
      console.log('所有文本段落处理完成');
      return;
    }
    
    const currentSegment = segments[index];
    console.log(`正在处理第${index + 1}/${segments.length}段文本:`, currentSegment);
    
    // 调用短文本语音合成API
    wx.request({
      url: 'https://test.uu163yun.com/mapi/Helper/Aip/CreateShortTts',
      method: 'POST',
      timeout: 10000, // 10秒超时
      header: {
        ...api.mapiHeaders(userToken, { ApiVersion: '5.0' }),
        'content-type': 'application/x-www-form-urlencoded'
      },
      data: {
        text: currentSegment,
        voice: voiceType, // 使用当前角色的声音类型
        type: 'Baidu',
        format: 'mp3'
      },
      success: (res) => {
        console.log(`第${index + 1}段语音合成响应:`, res);
        // 只要返回了base64数据就进行播放，不检查format字段
        if (res.data && res.data.base64) {
          // 语音合成成功，base64转为临时文件播放
          console.log(`第${index + 1}段语音合成完成`);
          
          // 播放下一段音频
          this.playAudioFromBase64(res.data.base64, 'mp3', () => {
            // 当前段播放完成后，处理下一段
            this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
          });
        } else if (res.data && res.data.result && res.data.reason) {
          // 处理API错误响应
          console.error(`第${index + 1}段语音合成失败：`, res.data.reason);
          wx.showToast({
            title: res.data.reason || '语音合成失败',
            icon: 'none'
          });
          // 继续处理下一段
          this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
        } else if (res.data && res.data.result === "exception") {
          // 特别处理操作超时等异常
          const errorMsg = res.data.reason || '操作超时，请稍后重试';
          console.error(`第${index + 1}段语音合成异常：`, errorMsg);
          wx.showToast({
            title: errorMsg,
            icon: 'none'
          });
          // 继续处理下一段
          this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
        } else {
          console.error(`第${index + 1}段语音合成失败：`, res);
          wx.showToast({
            title: '语音合成失败',
            icon: 'none'
          });
          // 继续处理下一段
          this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
        }
      },
      fail: (err) => {
        console.error(`第${index + 1}段语音合成请求失败：`, err);
        wx.showToast({
          title: '网络请求失败',
          icon: 'none'
        });
        // 继续处理下一段
        this.processAndPlayNextSegment(segments, index + 1, userToken, voiceType);
      }
    });
  },
  
  // 文本预处理：替换特殊字符或数字
  preprocessText(text) {
    if (!text || text.length === 0) return text;
    
    // 将652替换为六五二
    return text.replace(/652/g, '六五二');
  },
  
  // 将文本分段
  splitTextIntoSegments(text, maxLength) {
    if (!text || text.length === 0) return [];
    
    const segments = [];
    // 简单按字符数分割
    for (let i = 0; i < text.length; i += maxLength) {
      segments.push(text.substring(i, i + maxLength));
    }
    
    return segments;
  },

  // 手动播放/暂停语音
  togglePlayAudio() {
    if (this.data.isPlaying) {
      this.data.audioContext.pause();
    } else {
      // 需要确保已经有音频URL
      this.data.audioContext.play();
    }
  },

  // ============= 聊天功能 =============

  toggleHistoryPanel() {
    this.setData({ isHistoryShow: !this.data.isHistoryShow });
    
    // 当打开历史记录面板时，重新加载历史记录
    if (!this.data.isHistoryShow) {
      this.loadMoreHistory();
    }
  },

  updateTimeDivider() {
    const date = new Date();
    this.setData({
      currentTime: `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes()
        .toString().padStart(2, '0')}`
    });
  },

  onInputChange(e) {
    this.setData({ inputValue: e.detail.value });
  },

  sendMessage() {
    const content = this.data.inputValue;
    if (!content || this.data.isSending) return;

    if (!this.data.isConnected) {
      wx.showModal({
        title: '提示',
        content: '未连接到服务器，是否尝试重连',
        success: (res) => {
          if (res.confirm) this.connectWebSocket();
        }
      });
      return;
    }

    // 当用户发送新消息时，清空音频队列，实现伪打断功能
    this.clearAudioQueue();

    const newMessages = [...this.data.messages, { role: 'user', content }];
    this.setData({
      messages: newMessages,
      inputValue: '',
      isSending: true,
      showTimeDivider: false
    });

    this.scrollToBottom();
    this.sendChatMessage(content);
  },

  sendChatMessage(content) {
    const { userToken } = this.data;
    wx.request({
      url: api.endpoints.CHAT_ASK,
      method: 'POST',
      header: api.mapiHeaders(userToken),
      data: api.buildChatAsk(content),
      success: (res) => {
        console.log("消息发送成功:", res.data);
        this.setData({ isTyping: true });
      },
      fail: (err) => {
        console.error("消息发送失败:", err);
        wx.showToast({ title: '发送失败', icon: 'none' });
      },
      complete: () => {
        this.setData({ isSending: false });
      }
    });
  },

  scrollToBottom() {
    setTimeout(() => {
      this.setData({ scrollTop: 999999 });
    }, 100);
  },

  loadSessionHistory(e) {
    this.saveChattingHistory();
    this.setData({
      isTyping: false,
      currentReply: ""
    });
    const sessionId = e.currentTarget.dataset.sessionid;
    wx.request({
      url: api.endpoints.HISTORY_SESSION(this.data.userId, sessionId),
      method: 'GET',
      success: (res) => {
        console.log('会话详情加载成功:', res);
        if (res.data && res.data.code === 200) {
          this.setData({
            messages: res.data.data || [],
            sessionId: sessionId
          });
        } else {
          console.error('会话详情加载失败:', res);
          wx.showToast({
            title: '会话详情加载失败',
            icon: 'none'
          });
        }
      },
      fail: (error) => {
        console.error('会话详情加载失败:', error);
        wx.showToast({
          title: '会话详情加载失败',
          icon: 'none'
        });
      }
    });
  },

  loadMoreHistory() {
    if (this.data.isLoadingHistory) return;
    this.setData({ isLoadingHistory: true });
    wx.request({
      url: api.endpoints.HISTORY_SESSIONS(this.data.userId),
      method: 'GET',
      success: (response) => {
        console.log('聊天记录加载成功:', response);
        if (response.data && response.data.code === 200) {
          const formatted = (response.data.data || []).map(session => ({
            ...session,
            createTime: this.formatTime(session.createTime)
          }));
          this.setData({ sessionList: formatted });
        } else {
          console.error('聊天记录加载失败:', response);
          wx.showToast({
            title: '聊天记录加载失败',
            icon: 'none'
          });
        }
      },
      fail: (error) => {
        console.error('聊天记录加载失败:', error);
        wx.showToast({
          title: '聊天记录加载失败',
          icon: 'none'
        });
      },
      complete: () => {
        this.setData({ isLoadingHistory: false });
      }
    });
  },

  formatTime(timeStr) {
    if (!timeStr) return '';
    const date = new Date(timeStr);
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    const h = String(date.getHours()).padStart(2, '0');
    const mm = String(date.getMinutes()).padStart(2, '0');
    return `${y}-${m}-${d} ${h}:${mm}`;
  },

  onUnload() {
    // 标记页面已卸载（全局）
    const app = getApp();
    if (app && app.globalData) app.globalData.isUnloaded = true;

    // 新增：先发送SignOut命令
    const { websocket, isConnected, userId, sessionId ,userToken} = this.data;
    if (websocket && isConnected) {
      try {
        websocket.send({
          data: "SignOut",
          success: () => {
            console.log("已发送SignOut命令");
          },
          fail: (err) => {
            console.error("SignOut命令发送失败：", err);
          }
        });
      } catch (e) {
        console.error("SignOut命令异常：", e);
      }
    }

    // 延迟关闭WebSocket和后续操作，确保SignOut发出
    setTimeout(() => {
      if (websocket) {
        websocket.close();
        console.log("WebSocket连接已关闭");
      }
      this.saveChattingHistory();

      // 会话结束接口调用
      if (userId && sessionId) {
        wx.request({
          url: api.endpoints.CHAT_STOP,
          method: "POST",
          header: api.mapiHeaders(userToken, { ApiVersion: '5.0' }),
          data: api.buildChatStop(userId),
          success: (res) => {
            console.log("会话结束接口返回：", res.data);
          },
          fail: (err) => {
            console.error("会话结束接口调用失败：", err);
          }
        });
      }
      
      // 销毁音频上下文
      if (this.data.audioContext) {
        this.data.audioContext.destroy();
      }
      
      // 销毁所有活跃的音频上下文并清理临时文件
      if (this.activeAudioContexts) {
        this.activeAudioContexts.forEach(context => {
          try {
            context.destroy();
          } catch (e) {
            console.warn('销毁音频上下文时出错:', e);
          }
        });
        this.activeAudioContexts = [];
      }
      
      // 清理所有临时音频文件
      this.cleanTemporaryFiles();
      
      // 移除storage监听
      wx.onStorageChange(() => {});
    }, 100);
  },
  saveChattingHistory() {
    const { userId, sessionId, messages } = this.data;
    if (messages.length <= 1) return;
    if (!userId || !sessionId) return;

    wx.request({
      url: api.endpoints.HISTORY_SAVE,
      method: "POST",
      header: api.jsonHeaders(this.data.userToken),
      data: { userId, sessionId, messages },
      success: (res) => {
        if (res.data && res.data.code === 200) {
          console.log("聊天记录保存成功");
        }
      },
      fail: (err) => {
        console.error("聊天记录保存失败:", err);
        wx.setStorageSync(`unsaved_chat_${sessionId}`, { userId, sessionId, messages });
      }
    });
  },
  // 切换输入模式（文字/语音）
  toggleInputMode() {
    this.setData({
      isVoiceMode: !this.data.isVoiceMode
    });
  },

  // 开始录音
  startRecording() {
    if (this.data.recording) return;
    
    // 打断当前正在播放的语音
    this.clearAudioQueue();
    
    // 使用更符合百度语音识别要求的参数配置
    const options = {
      duration: 60000, // 最长60秒
      sampleRate: 16000, // 采样率16000Hz
      numberOfChannels: 1, // 单声道
      encodeBitRate: 48000, // 编码码率
      format: 'wav', // 使用wav格式
      frameSize: 50
    };
    
    this.data.recordManager.start(options);
  },

  // 停止录音
  stopRecording() {
    if (!this.data.recording) return;
    
    this.data.recordManager.stop();
  },

  // 取消录音
  cancelRecording() {
    if (!this.data.recording) return;
    
    this.data.recordManager.stop();
    this.setData({ recording: false });
  },

  // 将音频文件转换为base64
  convertAudioToBase64(filePath, duration) {
    wx.getFileSystemManager().readFile({
      filePath: filePath,
      success: (res) => {
        // 获取文件的原始字节长度
        const fileBytes = res.data;
        const len = fileBytes.byteLength;
        
        // 将音频文件转为base64编码
        const base64 = wx.arrayBufferToBase64(fileBytes);
        console.log('音频转base64成功，原始长度：', len);
        this.sendVoiceMessage(base64, duration, len);
      },
      fail: (err) => {
        console.error('音频转base64失败', err);
        wx.showToast({
          title: '语音识别失败',
          icon: 'none'
        });
      }
    });
  },

  // 发送语音消息
  sendVoiceMessage(base64, duration, len) {
    if (!this.data.isConnected) {
      wx.showModal({
        title: '提示',
        content: '未连接到服务器，是否尝试重连',
        success: (res) => {
          if (res.confirm) this.connectWebSocket();
        }
      });
      return;
    }

    // 当用户发送新消息时，清空音频队列，实现伪打断功能
    this.clearAudioQueue();

    // 添加用户语音消息到聊天记录
    const newMessages = [...this.data.messages, { 
      role: 'user', 
      content: '[语音消息]',
      voice: true,
      duration: Math.round(duration / 1000)
    }];
    
    this.setData({
      messages: newMessages,
      isSending: true,
      showTimeDivider: false
    });

    this.scrollToBottom();
    
    // 调用语音识别API
    this.recognizeSpeech(base64, len);
  },

  // 语音识别
  recognizeSpeech(base64, len) {
    const { userToken, userId } = this.data;
    
    wx.request({
      url: 'https://test.uu163yun.com/mapi/Helper/Aip/Asr',
      method: 'POST',
      header: {
        ...api.mapiHeaders(userToken, { ApiVersion: '5.0' }),
        'content-type': 'application/x-www-form-urlencoded'
      },
      data: {
        type: 'Baidu',
        base64: base64,
        rate: 16000, // 采样率16000Hz
        len: len, // 传递正确的原始字节长度
        format: 'wav', // 使用wav格式
        userGuid: userId,
        endpointCode: 'Suse652-1',
        endpointVersion: '1.0.0',
        dev_pid: 1537 // 使用普通话输入法模型
      },
      success: (res) => {
        console.log('语音识别响应:', res);
        if (res.data && res.data.texts) {
          // 识别成功，发送文本消息
          this.sendTextMessage(res.data.texts);
        } else if (res.data && res.data.result === "NotRecongnized") {
          // 识别失败，显示具体错误信息
          wx.showToast({
            title: res.data.reason || '语音识别失败',
            icon: 'none'
          });
          // 输出详细错误信息到控制台，便于调试
          console.log('语音识别失败详情:', {
            reason: res.data.reason,
            result: res.data.result,
            texts: res.data.texts
          });
        } else {
          wx.showToast({
            title: '语音识别失败',
            icon: 'none'
          });
        }
      },
      fail: (err) => {
        console.error('语音识别请求失败', err);
        wx.showToast({
          title: '网络请求失败',
          icon: 'none'
        });
      },
      complete: () => {
        this.setData({ isSending: false });
      }
    });
  },

  // 发送文本消息
  sendTextMessage(content) {
    const newMessages = [...this.data.messages];
    // 更新最后一条消息的内容
    if (newMessages.length > 0 && newMessages[newMessages.length - 1].voice) {
      newMessages[newMessages.length - 1].content = content;
      this.setData({ messages: newMessages });
    }
    
    this.sendChatMessage(content);
  },
  
  // 处理用户切换
  handleUserSwitch() {
    // 监听 storage 变化，检测用户切换
    wx.onStorageChange((res) => {
      if (res.key === 'loginResult') {
        // 用户信息发生变化，清空音频队列
        this.clearAudioQueue();
      }
    });
  },

  // 播放视频
  playVideo() {
    this.videoContext = wx.createVideoContext("chatBgvideo", this);
    if (this.videoContext) {
      try {
        this.videoContext.play();
      } catch (e) {
        console.warn("视频播放失败:", e);
      }
    }
  },
});